// OSRawLock.h

#ifndef _OSRawLock_h_
#define _OSRawLock_h_

#if defined(_MSC_VER) && (_MSC_VER > 1000)
	#pragma once
#endif

#if defined(WIN32) && !defined(_WINDOWS_)
	#ifndef _WINSOCKAPI_
		#define _WINSOCKAPI_   /* Prevent inclusion of winsock.h in windows.h */
	#endif
	#include <windows.h>
#endif

#if defined(__linux__)
	#include <pthread.h>
#endif

class COSRawLock
{
#if defined(WIN32)
	CRITICAL_SECTION _os_mutex;
#elif defined(__linux__)
	pthread_mutex_t _os_mutex;
#endif

private:
	COSRawLock(const COSRawLock&);
	COSRawLock& operator= (const COSRawLock&);

public:
	COSRawLock()
	{
#if defined(WIN32)
		InitializeCriticalSection(&_os_mutex);
#elif defined(__linux__)
	#ifdef __USE_GNU
		pthread_mutex_t m = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
	#else
		#ifdef __cplusplus
		pthread_mutex_t m = {0, 0, 0, PTHREAD_MUTEX_RECURSIVE_NP, {0, 0}};
		#else
		pthread_mutex_t m = {{0, 0, 0, PTHREAD_MUTEX_RECURSIVE_NP, 0, {0}}};
		#endif
	#endif
		_os_mutex = m;
#endif
	}
	~COSRawLock()
	{
#if defined(WIN32)
		DeleteCriticalSection(&_os_mutex);
#endif
	}

	void Lock()
	{
#if defined(WIN32)
		EnterCriticalSection(&_os_mutex);
#elif defined(__linux__)
		pthread_mutex_lock(&_os_mutex);
#endif
	}
	void Unlock()
	{
#if defined(WIN32)
		LeaveCriticalSection(&_os_mutex);
#elif defined(__linux__)
		pthread_mutex_unlock(&_os_mutex);
#endif
	}
	BOOL TryLock(DWORD timeout = 0)
	{
		if(INFINITE == timeout)
		{
			Lock();
			return true;
		}
		else if(0 == timeout)
			return TryLock_();
		else
		{
			if(TryLock_())
				return true;
			DWORD tick0 = GetTickCount();
			while(true)
			{
				Sleep(1);
				if(TryLock_())
					return true;
				else if(GetTickCount() - tick0 >= timeout)
					break;
			}
			return false;
		}
	}

private:
	BOOL TryLock_()
	{
		BOOL ok = FALSE;
#if defined(WIN32)
		typedef BOOL (WINAPI* FUNC_TRYENTER)(CRITICAL_SECTION*);
		static FUNC_TRYENTER TryEnter = NULL;
		if(NULL == TryEnter)
		{
			HMODULE h = GetModuleHandle(_T("Kernel32.dll"));
			if( NULL == h
				|| NULL == (TryEnter = (FUNC_TRYENTER)GetProcAddress(h, "TryEnterCriticalSection"))
				)
			{
				ASSERT(FALSE);
				return FALSE;
			}
		}
		ok = TryEnter(&_os_mutex);
#elif defined(__linux__)
		int r = pthread_mutex_trylock(&_os_mutex);
		if(r<0)
		{
			ASSERT(errno == EBUSY);
			return FALSE;
		}
		ok = TRUE;
#endif
		return ok;
	}
};

#endif	// #ifndef _OSRawLock_h_
